package cate.game.train.equip;

import cate.common.table.d.*;
import cate.common.table.item.GodEquipPumpItemRow;
import cate.common.table.item.SpiceLevelUpRow;
import cate.common.table.item.equip.row.EquipBaseRow;
import cate.common.util.GameResult;
import cate.common.util.XT;
import cate.game.GameBody;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.RoleEventPublisher;
import cate.game.event.hero.HeroPowerChangeEvent;
import cate.game.res.*;
import cate.game.role.Role;
import cate.game.role.bag.equip.Equip;
import cate.game.role.bag.equip.msg.EquipInitResp;
import cate.game.role.bag.equip.msg.EquipSellResp;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.vo.UidNumVO;
import cate.game.train.equip.msg.*;
import easy.java.practice.errorcode.EcResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * 装备培养处理器
 */
@Slf4j
public class TrainEquipHandler implements RoleEventPublisher {
    public void sellEquip(Role role, List<UidNumVO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        EquipSellResp resp = new EquipSellResp();
        resp.reward = new MixRes();
        RewardShow backReward = new RewardShow();
        for (UidNumVO param : list) {
            Equip equip = role.getBag().getEquip().getItem(param.uid);
            if (equip == null) {
                role.getGame().notice.message(role, "装备不存在");
                continue;
            }
            if (StringUtils.isBlank(equip.getTpl(role.toPlayBuildContext()).resolveRewardStr)) {
                role.getGame().notice.message(role, "装备不可出售");
                continue;
            }
            GameResult<RewardShow> r = role.getBag().getEquip().removeItemByUid(param.uid, param.num, null);
            if (r.ok()) {
                backReward.merge(r.data);
                EquipBaseRow row = equip.getTpl(role.toPlayBuildContext());
                resp.reward.addList(new MixRes(row.resolveRewardStr).multiply(param.num));
                resp.num += param.num;
            } else {
                role.getGame().notice.message(role, r, true);
            }
        }

        if (resp.num > 0) {
            resp.reward.add(role, true);
            if (backReward.isEmpty()) {
                role.getGame().notice.dialogReward(role, resp.reward);
            } else {
                backReward.addTplRwd(resp.reward);
                role.getGame().notice.dialog(role, backReward);
            }
            role.sendNow(resp);
        }
    }

    private Equip findEquip(Role role, String heroUid, String equipUid, byte pos) {
        Equip equip;
        if (StringUtils.isNotBlank(heroUid)) {
            Hero hero = role.getBag().hero.getItem(heroUid);
            if (hero == null) {
                return null;
            }
            equip = hero.equipsOn.getPosItems().get(pos);
        } else {
            equip = role.getBag().getEquip().getItem(equipUid);
        }
        return equip;
    }

    private Equip findEquip(Role role, String equipUid) {
        return findEquip(role, null, equipUid, (byte) 0);
    }

    public Hero getHero(Role role, String heroUid) {
        return role.getBag().hero.getItem(heroUid);
    }

    /**
     * 装备拓印
     *
     * @param heroUid  英雄唯一id
     * @param equipUid 装备唯一id
     */
    public void rubbingEquip(Role role, String heroUid, String equipUid, byte pos) {
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            role.getGame().notice.message(role, "该装备不存在");
            return;
        }
        EquipBaseRow row = equip.getTpl(role.getGame());
        if (row == null || !row.isOverstep) {
            role.getGame().notice.message(role, "该装备不可铭刻");
            return;
        }
        if (equip.campMark > 0) {
            role.getGame().notice.message(role, "没有阵营印记的装备才可铭刻");
            return;
        }
        EcResult<MixResItem> consumeRes = new MixRes(row.rubbingConsumeStr).consume(role, null);
        if (!consumeRes.ok()) {
            role.getGame().notice.tipResItem(role, consumeRes.data);
            return;
        }
        equip.campMark = GDEquip.randomCampMark();
        role.sendNow(new EquipRubbingResp(equip, heroUid, pos));
        if (StringUtils.isNotBlank(heroUid)) {
            role.getBase().updatePower();
            Hero hero = role.getBag().hero.getItem(heroUid);
            if (equip.campMark == hero.getTpl(role.getGame()).camp) {
                publishEvent(role, new HeroPowerChangeEvent(role, hero));
            }
        }
    }

    /**
     * 装备重铸
     *
     * @param heroUid  英雄uid
     * @param equipUid 装备uid
     * @param pos      位置
     */
    public void reforgeEquip(Role role, String heroUid, String equipUid, byte pos) {
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            role.getGame().notice.message(role, "不存在的装备");
            return;
        }
        EquipBaseRow row = equip.getTpl(role.getGame());
        if (equip.campMark <= 0) {
            role.getGame().notice.message(role, "带有种族印记的装备才可重铸");
            return;
        }
        EcResult<MixResItem> consumeRes = new MixRes(row.reforgeConsumeStr).consume(role, null);
        if (!consumeRes.ok()) {
            role.getGame().notice.tipResItem(role, consumeRes.data);
            return;
        }
        List<Byte> list = new ArrayList<>();
        for (byte b : GDCamp.Type.getAllCamps(false)) {
            if (b != equip.campMark) {
                list.add(b);
            }
        }
        equip.campMark = list.get(XT.randomIndex(list.size()));
        role.sendNow(new EquipReforgeResp(equip, heroUid, pos));
        if (StringUtils.isNotBlank(heroUid)) {
            role.getBase().updatePower();
            Hero hero = role.getBag().hero.getItem(heroUid);
            if (equip.campMark == hero.getTpl(role.getGame()).camp) {
                publishEvent(role, new HeroPowerChangeEvent(role, hero));
            }
        }
    }

    public GameResult<Void> overstep(Role role, String equipUid, String heroUid, byte pos) {
        GameResult<Void> r = new GameResult<>();
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            return r.fail("装备不存在");
        }
        EquipBaseRow equipRow = equip.getTpl(role.getGame());
        if (equipRow == null || equipRow.overstepId == 0) {
            return r.fail("该装备无法超越");
        }
        if (equipRow.isOverstep) {
            return r.fail("该装备已经超越了");
        }
        GameResult<MixResItem> consume = new MixRes(equipRow.overstepConsumeStr).consume(role);
        if (!consume.ok()) {
            role.getGame().notice.tipResItem(role, consume.data);
            return r.fail(consume.message);
        }
        long needChange = equip.num - 1;
        if (needChange > 0) {
            Equip e = new Equip(role, equipRow.id, needChange);
            role.getBag().getEquip().addItem(e, null);
            equip.num = 1;
        }
        equip.tid = equipRow.overstepId;
        role.sendNow(new EquipOverstepResp(equip, heroUid, pos));
        return r;
    }

    public GameResult<Void> back(Role role, String equipUid, String heroUid, byte pos) {
        GameResult<Void> r = new GameResult<>();
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            return r.fail("装备不存在");
        }
        EquipBaseRow equipRow = equip.getTpl(role.getGame());
        if (equipRow == null) {
            return r.fail("该装备无法回退");
        }
        if (!equipRow.isOverstep) {
            return r.fail("该装备未超越");
        }
        EquipBaseRow preOverstepRow = role.getGame().table.equip.base.getPreOverstepEquipRow(equipRow.id);
        if (preOverstepRow == null) {
            return r.fail("该装备无法回退");
        }
        EquipBaseRow overstepRow = role.getGame().table.equip.base.get(preOverstepRow.overstepId);
        if (overstepRow == null) {
            return r.fail("该装备无法回退");
        }
        equip.tid = preOverstepRow.id;
        equip.campMark = 0;
        new MixResAdder()
                .setOperation(GDOperation.EQUIP_BACK)
                .setOperationSub(equipRow.id)
                .setDialogReward(true)
                .setSendMailWhenFull(true)
                .setRes(overstepRow.overstepBackStr)
                .exe(role);
        role.sendNow(new EquipBackResp(equip, heroUid, pos));
        return r;
    }

    public GameResult<Void> forgeOverstepEquip(Role role, int tid, Set<String> equipUids) {
        GameResult<Void> r = new GameResult<>();
        EquipBaseRow row = role.getGame().table.equip.base.get(tid);
        if (row == null || !row.isOverstep || StringUtils.isBlank(row.forgeRes)) {
            return r.fail("该装备无法被合成");
        }
        MixRes equipConsume = new MixRes(row.forgeRes);
        equipConsume.items.removeIf(e -> e.type != GDObj.Type.EQUIP);
        if (equipConsume.items.stream().mapToLong(e -> e.num).sum() != equipUids.size()) {
            return r.fail("装备数量选择有误");
        }
        MixRes numConsume = new MixRes(row.forgeRes);
        numConsume.items.removeIf(e -> e.type == GDObj.Type.EQUIP);
        GameResult<MixResItem> numConsumeRes = numConsume.consumeCheck(role);
        if (!numConsumeRes.ok()) {
            role.getGame().notice.tipResItem(role, numConsumeRes.data);
            return r.fail(numConsumeRes);
        }
        int campMarkNum = 0;
        for (String equipUid : equipUids) {
            Equip cost = findEquip(role, null, equipUid, (byte) 0);
            if (cost == null) {
                return r.fail("错误的装备材料");
            }
            equipConsume.minusItem(new MixResItem(GDObj.Type.EQUIP, cost.tid, 1));
            if (cost.campMark > 0) {
                campMarkNum++;
            }
        }
        if (!equipConsume.isEmpty()) {
            return r.fail("错误的装备材料");
        }
        numConsume.consumeDirect(role, null);
        RewardShow rewardShow = new RewardShow();
        MergeMsgSender sender = new MergeMsgSender();
        for (String equipUid : equipUids) {
            GameResult<RewardShow> removeRes = role.getBag().getEquip().removeItem(equipUid, sender);
            if (removeRes.ok()) {
                rewardShow.merge(removeRes.data);
            }
        }
        sender.send(role, true);
        // 添加装备
        Equip equip = new Equip(role, tid, 1);
        if (GDEquip.reforgeCampResult(campMarkNum)) {
            equip.campMark = GDEquip.randomCampMark();
        }
        role.getBag().getEquip().addItem(equip, null);
        rewardShow.addInsRwd(GDObj.Type.EQUIP, equip.uid);
        role.getGame().notice.dialog(role, rewardShow);
        return r;
    }

    /**
     * 普通锻造装备
     */
    public GameResult<Void> forgeNormalEquip(Role role, int id, int num) {
        GameResult<Void> r = new GameResult<>();
        EquipBaseRow row = role.getGame().table.equip.base.get(id);
        if (row == null || row.isOverstep) {
            return r.fail("装备无法锻造");
        }
        if (num <= 0) {
            return r.fail("合成数量有误");
        }
        MixRes consume = new MixRes(row.forgeRes).multiply(num);
        if (consume.isEmpty()) {
            return r.fail("合成消耗配置错误");
        }
        GameResult<MixResItem> r1 = consume.consumeCheck(role, null);
        if (!r1.ok()) {
            role.getGame().notice.message(role, r1.message);
            return r.fail(r1);
        }
        MixRes reward = new MixRes();
        reward.addItem(new MixResItem(GDObj.Type.EQUIP, id, num));
        r = reward.addCheck(role);
        if (!r.ok()) {
            return r;
        }
        MergeMsgSender sending = new MergeMsgSender();
        consume.consumeDirect(role, null, sending);
        sending.send(role, false);
        reward.add(role, true, GDOperation.EQUIP_FORGE, id);
        role.getGame().notice.dialogReward(role, reward);
        role.getHistory().action.equipComposeTimes();
        role.getHistory().action.equipCompose(row.quality, num);
        role.getHistory().action.equipComposeStar(row.star, num);
        return r;
    }

    /**
     * 一键合成
     */
    public void batchForgeNormalEquip(Role role, byte pos) {
        // 将原有的装备列表存一份，用于一键合成完后比较数量的变化
        List<Equip> oldList = new ArrayList<>();
        for (Equip equip : role.getBag().getEquip().getList()) {
            // 精炼装备无法合成
            if (!equip.getTpl(role.getGame()).isOverstep) {
                oldList.add(equip.copy());
            }
        }

        // 累计消耗的金币
        MixRes consume = new MixRes();
        MergeMsgSender sending = new MergeMsgSender();

        // 根据部位id合成装备
        for (EquipBaseRow row : role.getGame().table.equip.base.getList()) {
            if (row.pos == pos && !row.isOverstep) {
                if (role.getRes().forge.canForge(row.pos, row.quality, row.star)) {
                    forgeSingleNormalEquip(role, row, consume, sending);
                }
            }
        }
        sending.send(role, false);
        // 一键合成完后新增的装备
        MixRes equipReward = new MixRes();
        for (Equip po : role.getBag().getEquip().getList()) {
            EquipBaseRow row = po.getTpl(role.getGame());
            if (row.pos == pos && !row.isOverstep) {
                long changeNum = po.num;
                for (Equip oldPo : oldList) {
                    if (po.tid == oldPo.tid) {
                        changeNum = po.num - oldPo.num;
                    }
                }
                // 判断装备的数量是否增多，增多了说明是本次合成获得的
                if (changeNum > 0) {
                    equipReward.addItem(new MixResItem(GDObj.Type.EQUIP, po.tid, changeNum));
//                    if (row.quality == 5) {
//                        role.getHistory().action.forgeRedEquipStar(row.star, changeNum);
//                    }
//                    role.getHistory().action.forgeRedEquipStarAndPos(row.star, row.pos, row.quality);
                    role.getHistory().action.equipCompose(row.quality, (int) changeNum);
                    role.getHistory().action.equipComposeStar(row.star, (int) changeNum);
                }
            }
        }
        if (equipReward.isEmpty()) {
            role.getGame().notice.message(role, "没有足够的资源合成装备");
            return;
        }
        role.getGame().notice.dialogReward(role, equipReward);
        role.getHistory().action.equipComposeTimes();
    }

    /**
     * 尝试合成一种装备直到资源耗尽
     */
    private void forgeSingleNormalEquip(Role role, EquipBaseRow row, MixRes consume, MergeMsgSender sending) {
        MixRes forgeRes = new MixRes(row.forgeRes);
        if (forgeRes.isEmpty()) {
            return;
        }
        int num = 0;
        while (forgeRes.copy().multiply(num).consumeCheck(role, null).ok()) {
            num++;
        }
        num--;
        if (num <= 0) {
            return;
        }
        EcResult r = forgeRes.multiply(num).consume(role, null, sending);
        if (r.ok()) {
            consume.addList(forgeRes.copy().multiply(num));
            // 发奖励
            MixRes reward = new MixRes();
            reward.addItem(new MixResItem(GDObj.Type.EQUIP, row.id, num));
            reward.add(role, sending);
        }
    }

    public void onEquip(Role role, String heroUid, Set<String> equipUidList) {
        Hero hero = role.getBag().hero.getItem(heroUid);
        if (hero == null) {
            role.getGame().notice.message(role, "英雄不存在");
            return;
        }
        if (equipUidList == null || equipUidList.isEmpty()) {
            role.getGame().notice.message(role, "装备不存在");
            return;
        }
        if (hero.getEffectiveLevel() < GDEquip.EQUIP_OPEN_LEVEL) {
            role.getGame().notice.message(role, "英雄" + GDEquip.EQUIP_OPEN_LEVEL + "级后才可以穿戴装备");
            return;
        }
        HeroEquipOnResp resp = new HeroEquipOnResp();
        resp.heroUid = heroUid;
        for (String equipUid : equipUidList) {
            Equip e = role.getBag().getEquip().getItem(equipUid);
            if (e == null) {
                log.error("请求穿戴一个不存在的装备id={}, list={}", equipUid, equipUidList);
                continue;
            }
            EcResult<Byte> r = hero.onEquip(role.toPlayBuildContext(), e);
            if (!r.ok()) {
                role.getGame().notice.message(role, r.message);
            } else {
                resp.equipMap.put(r.data, hero.equipsOn.getPosItems().get(r.data));
            }
        }
        role.getBase().updatePower();
        role.sendNow(resp);
        role.getHistory().action.heroEquipOn(equipUidList.size());
        role.getHistory().action.equipsQualityOnHero(hero);
        role.getHistory().action.equipQualityStarOn();
    }

    //神装注灵
    public void pumpPsionic(Role role, String heroUid, String equipUid, byte pos, Map<String, Integer> costUids, MixRes consume) {
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            role.getGame().notice.message(role, "该装备不存在");
            return;
        }
        if ((costUids == null || costUids.isEmpty()) && (consume == null || consume.isEmpty())) {
            role.getGame().notice.message(role, "至少吞噬一件装备或材料");
            return;
        }
//        if (costUids != null && !costUids.isEmpty() && costUids.values().stream().mapToInt(v -> v).sum() > 15) {
//            role.getGame().notice.message(role, "最多可同时选15件装备");
//            return;
//        }
        EquipBaseRow tpl = equip.getTpl(role.toPlayBuildContext());
        if (tpl == null) {
            role.getGame().notice.message(role, "装备配置错误");
            return;
        }
        if (tpl.canPump != 1) {
            role.getGame().notice.message(role, "只有神装可进行注灵");
            return;
        }
        if (equip.psionicExp >= tpl.pumpLimit) {
            role.getGame().notice.message(role, "注灵已满");
            return;
        }
        int expAdd = 0;
        if (costUids != null) {
            for (String uid : costUids.keySet()) {
                Equip costEquip = findEquip(role, uid);
                if (costEquip == null) {
                    role.getGame().notice.message(role, "吞噬的装备不存在");
                    return;
                }
                EquipBaseRow baseRow1 = costEquip.getTpl(role.getGame());
                if (baseRow1 == null) {
                    role.getGame().notice.message(role, "吞噬的装备有误");
                    return;
                }
                int num = costUids.get(uid);
                if (num > costEquip.num) {
                    role.getGame().notice.message(role, "吞噬的数量有误");
                    return;
                }
                // 获取被吞噬时的资源返还
                expAdd += costEquip.consumeExp() * num;
            }
        }
        if (consume != null) {
            for (MixResItem item : consume.items) {
                if (item.type != GDObj.Type.PROP) {
                    role.getGame().notice.message(role, "吞噬的星纹秘银有误");
                    return;
                }
                GodEquipPumpItemRow pumpItemRow = role.getGame().table.item.pumpItem.get(item.tid);
                if (pumpItemRow == null) {
                    role.getGame().notice.message(role, "吞噬的星纹秘银有误");
                    return;
                }
                expAdd += pumpItemRow.value * item.num;
            }
        }
        if (consume != null) {
            EcResult<MixResItem> r = consume.consume(role, null);
            if (!r.ok()) {
                role.getGame().notice.message(role, r.message);
                return;
            }
        }
        if (costUids != null) {
            if (!costUids.isEmpty()) {
                MergeMsgSender sender = new MergeMsgSender();
                for (String uid : costUids.keySet()) {
                    role.getBag().getEquip().removeItemByUid(uid, costUids.get(uid), sender);
                }
                sender.send(role, true);
            }
        }

        int backExp = equip.addExp(expAdd);
        MixRes reward = new MixRes();
        reward.addList(calRwdByExp(role.getGame(), backExp));
        reward.addAndDialog(role,true,GDOperation.EQUIP_PUMP);
        role.sendNow(new EquipPumpResp(equip, heroUid, pos));
        role.getBase().updatePower();
        publishEvent(role, new HeroPowerChangeEvent(
                role, getHero(role, heroUid)));
    }

    //神装突破
    public void breakthrough(Role role, String heroUid, String equipUid, byte pos, CostSelect cs) {
        Equip equip = findEquip(role, heroUid, equipUid, pos);
        if (equip == null) {
            role.getGame().notice.message(role, "该装备不存在");
            return;
        }
        EquipBaseRow tpl = equip.getTpl(role.toPlayBuildContext());
        if (tpl == null) {
            role.getGame().notice.message(role, "装备配置错误");
            return;
        }
        if (equip.psionicExp < tpl.pumpLimit) {
            role.getGame().notice.message(role, "注灵未满，不可突破");
            return;
        }
        if (tpl.breakthroughTarget == 0) {
            role.getGame().notice.message(role, "已经达到极限啦！");
            return;
        }
        if (StringUtils.isBlank(tpl.breakthroughCost)) {
            role.getGame().notice.message(role, "突破消耗未配置");
            return;
        }
        if (CollectionUtils.isEmpty(cs.getUidLists(GDObj.Type.EQUIP))) {
            role.getGame().notice.message(role, "参数错误");
            return;
        }
        MixRes require = new MixRes(tpl.breakthroughCost);
        GameResult<MixResItem> consumeCheck = require.consumeCheck(role, cs);
        if (!consumeCheck.ok()) {
            role.getGame().notice.message(role, consumeCheck);
            return;
        }
        int expAdd = 0;
        for (String uid : cs.getUidLists(GDObj.Type.EQUIP)) {
            Equip costEquip = findEquip(role, uid);
            if (costEquip == null) {
                role.getGame().notice.message(role, "吞噬的装备不存在");
                return;
            }
            EquipBaseRow baseRow1 = costEquip.getTpl(role.getGame());
            if (baseRow1 == null) {
                role.getGame().notice.message(role, "吞噬的装备有误");
                return;
            }
            if (baseRow1.canPump != 1) {
                role.getGame().notice.message(role, "必须消耗神装突破");
                return;
            }
            // 获取被吞噬时的资源返还
            expAdd += costEquip.psionicExp;
        }
        require.consumeDirect(role, cs);
        equip.breakthrough();
        if (expAdd > 0) {
            equip.addExp(expAdd);
        }
        role.sendNow(new EquipBreakThroughResp(equip, heroUid, pos));
        role.getGame().notice.dialogReward(role, new MixRes().addItem(new MixResItem(equip.type, equip.tid, 1)));
        role.getBase().updatePower();
        publishEvent(role, new HeroPowerChangeEvent(
                role, getHero(role, heroUid)));
        if (StringUtils.isNotBlank(heroUid)) {
            role.getHistory().action.equipQualityStarOn();
        }
        role.getHistory().action.godEquipBreakUp(equip.getTpl(role.getGame()).star);
    }

    private MixRes calRwdByExp(GameBody game, int exp) {
        MixRes reward = new MixRes();
        if (exp > 0) {
            List<GodEquipPumpItemRow> list = game.table.item.pumpItem.list;
            list.sort(Comparator.comparing(e->-e.value));
            for(GodEquipPumpItemRow row : list){
                int bigExpNum = exp / row.value;
                if(bigExpNum > 0){
                    reward.addItem(new MixResItem(GDObj.Type.PROP, row.id, bigExpNum));
                    exp -= row.value * bigExpNum;
                }
            }
        }
        return reward;
    }
}
